/*
 * Copyright (C) 2022-2022. Huawei Technologies Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.openlookeng.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.ssh.JschRuntimeException;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.openlookeng.core.boot.config.FileConfig;
import com.openlookeng.core.boot.utils.Constant;
import com.openlookeng.core.dto.SshUserDTO;
import com.openlookeng.entity.Ecs;
import com.openlookeng.entity.OpenlookengCluster;
import com.openlookeng.entity.SysFile;
import com.openlookeng.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class LinuxShellUtils
{
    private LinuxShellUtils()
    {
    }

    public static String shellPath = "/opt/openlookengshell/"; //script path
    public static String deployPath = "/opt/openlookeng/"; //deployment path
    public static String defaultUserName = "openlookeng"; //openlookeng deploy user

    public static String execCmd(SshUserDTO sshUserDTO, String cmd)
    {
        try {
            Session session = JschUtil.createSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
            session.connect(5000);
            String res = JschUtil.exec(session, cmd, Charset.defaultCharset());
            session.disconnect();
            return res;
        }
        catch (Exception e) {
            log.error("execCmd err:{}", e.getMessage());
            throw new BizException(sshUserDTO.getSshHost() + "主机连接失败");
        }
    }

    public static Session getSession(SshUserDTO sshUserDTO) throws JSchException
    {
        Session session = JschUtil.createSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
        session.connect(5000);
        return session;
    }

    /**
     * 删除指定目录下的文件
     */
    public static void removeDirectory(SshUserDTO sshUserDTO, String deployPath) throws SftpException
    {
        try {
            Session session = getSession(sshUserDTO);
            Sftp sftp = JschUtil.createSftp(session);
            sftp.delDir(deployPath);
            sftp.delDir(shellPath);
            log.info("delete host:{}deployment directory:{},shell script directory:{}complete", sshUserDTO.getSshHost(), deployPath, shellPath);
            sftp.close();
        }
        catch (Exception e) {
            log.info("Delete host:{}Deployment directory:{},shell script directory:{}Exception:{}", sshUserDTO.getSshHost(), deployPath, shellPath, e.getMessage());
        }
    }

    /**
     * Initialize the sqllite database file
     *
     * @param fileDir
     */
    public static void initDbFile(String fileDir)
    {
        String dbFilePath = fileDir + "/open_lookeng_manager.db";
        File file = new File(dbFilePath);
        if (!file.exists()) {
            InputStream shellInputStream = LinuxShellUtils.class.getClassLoader().getResourceAsStream("open_lookeng_manager.db");
            FileUtil.writeFromStream(shellInputStream, dbFilePath);
        }
    }

    /**
     * Get memory, cpu, disk information
     *
     * @param sshUserDTO
     * @return
     */
    public static Map<String, String> getOsInfo(SshUserDTO sshUserDTO)
    {
        Map<String, String> res = new HashMap();
        try {
            ping(sshUserDTO.getSshHost(), sshUserDTO.getSshPort());
        }
        catch (Exception e) {
            log.error("ping err:{} ", e.getMessage());
            return res;
        }
        Session session = null;
        List<String> splitName = new ArrayList<>();
        splitName.add("disk_used");
        splitName.add("disk_total");
        splitName.add("disk_name");
        splitName.add("disk_percent");
        splitName.add("disk_mount");
        try {
            List<String> fileList = listFile(sshUserDTO, shellPath);
            if (!fileList.contains("osInfo.sh")) {
                String filePath = FileConfig.fileDir + shellPath + "/osInfo.sh";
                File file = new File(filePath);
                //If it does not exist locally
                if (!file.exists()) {
                    InputStream shellInputStream = LinuxShellUtils.class.getClassLoader().getResourceAsStream("shell/openlookeng/osInfo.sh");
                    file = FileUtil.writeFromStream(shellInputStream, file);
                }
                uploadFileToLinux(sshUserDTO, file, shellPath);
                String cmdStr = "chmod +x " + shellPath + "*.sh";
                session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
                JschUtil.exec(session, cmdStr, Charset.defaultCharset());
                session.disconnect();
            }
            if (!fileList.contains("getNetItem.sh")) {
                String filePath = FileConfig.fileDir + shellPath + "/getNetItem.sh";
                File file = new File(filePath);
                //If it does not exist locally
                if (!file.exists()) {
                    InputStream shellInputStream = LinuxShellUtils.class.getClassLoader().getResourceAsStream("shell/openlookeng/getNetItem.sh");
                    file = FileUtil.writeFromStream(shellInputStream, file);
                }
                uploadFileToLinux(sshUserDTO, file, shellPath);
                String cmdStr = "chmod +x " + shellPath + "*.sh";
                session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
                JschUtil.exec(session, cmdStr, Charset.defaultCharset());
                session.disconnect();
            }
            session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
            String cmd = "sed -i \"s/\\r//\" " + shellPath + "*.sh";
            JschUtil.exec(session, cmd, Charset.defaultCharset());
            String osDistInfo = JschUtil.exec(session, shellPath + "osInfo.sh", Charset.defaultCharset()).toLowerCase();
            if (StringUtils.isNotBlank(osDistInfo)) {
                osDistInfo = osDistInfo.replaceAll("\\n", "");
                String[] infos = osDistInfo.split("&");
                Arrays.stream(infos).forEach(t -> {
                    String[] datas = t.split("=");
                    String key = datas[0];
                    key = key.replaceAll(" ", "");
                    String val = datas.length > 1 ? datas[1].replace("\r", "") : null;
                    if (splitName.contains(key)) {
                        val = val.replaceAll(" ", "&");
                        res.put(key, val);
                    }
                    else {
                        res.put(key, val);
                    }
                });
            }
            session.disconnect();
        }
        catch (Exception e) {
            if (session != null) {
                session.disconnect();
            }
            log.error(" host:{},user:{},getOsInfo err:{}", sshUserDTO.getSshHost(), sshUserDTO.getSshUser(), e.getMessage());
        }
        return res;
    }

    private static void installNecCom(Session session, Map<String, String> res, SshUserDTO sshUserDTO)
    {
        ThreadUtil.execute(new Runnable() {
            @Override
            public void run()
            {
                String pm = res.get(Constant.PM.getCode());
                String python = res.get(Constant.PYTHON_VER.getCode()).replaceAll("\\.", "").replaceAll("_", "");
                String sshPass = res.get(Constant.SSH_PASS_VER.getCode()).replaceAll("\\.", "").replaceAll("_", "");
                String java = res.get(Constant.JAVA_VER.getCode()).replaceAll("\\.", "").replaceAll("_", "");
                String cmd = null;
                try {
                    Long.valueOf(python);
                }
                catch (Exception e) {
                    if ("apt".equals(pm)) {
                        cmd = Constant.INSTALL_PYTHON3_APT.getCode();
                    }
                    else {
                        cmd = Constant.INSTALL_PYTHON3_YUM.getCode();
                    }
                    JschUtil.exec(session, cmd, Charset.defaultCharset()).toLowerCase();
                }
                try {
                    Long.valueOf(sshPass);
                }
                catch (Exception e) {
                    if ("apt".equals(pm)) {
                        cmd = Constant.INSTALL_SSHPASS_APT.getCode();
                    }
                    else {
                        cmd = Constant.INSTALL_SSHPASS_YUM.getCode();
                    }
                    try {
                        JschUtil.exec(session, cmd, Charset.defaultCharset()).toLowerCase();
                    }
                    catch (Exception ee) {
                        ee.printStackTrace();
                    }
                }
                try {
                    Long.valueOf(java);
                }
                catch (Exception e) {
                    if ("apt".equals(pm)) {
                        cmd = Constant.INSTALL_JAVA_APT.getCode();
                    }
                    else {
                        cmd = Constant.INSTALL_JAVA_YUM.getCode();
                    }
                    JschUtil.exec(session, cmd, Charset.defaultCharset()).toLowerCase();
                }
            }
        });
    }

    public static boolean addDefaultUser(SshUserDTO sshUserDTO)
    {
        return addLinuxUser(sshUserDTO, defaultUserName);
    }

    /**
     * create linux user
     *
     * @param sshUserDTO
     * @param userName
     * @return
     */
    public static boolean addLinuxUser(SshUserDTO sshUserDTO, String userName)
    {
        Map<String, String> resMap = getOsInfo(sshUserDTO);
        Session session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
        String cmd = "";
        if (!resMap.getOrDefault("pm", "").contains("apt")) {
            //not ubuntu
            cmd = "useradd -d /opt/" + userName + " -m " + userName + " && echo " + userName + "| passwd --stdin " + userName;
        }
        else {
            cmd = "useradd -d /opt/" + userName + " -m " + userName + " && echo " + userName + ":" + userName + " | chpasswd";
        }
        JschUtil.exec(session, cmd, Charset.defaultCharset());
        session.disconnect();
        sshUserDTO.setSshUser(userName);
        sshUserDTO.setSshPass(userName);
        try {
            session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
            session.disconnect();
            return true;
        }
        catch (Exception e) {
            session.disconnect();
            return false;
        }
    }

    /**
     * Get the files in the specified directory
     *
     * @param sshUserDTO
     * @param path
     * @return
     */
    public static List<String> listFile(SshUserDTO sshUserDTO, String path)
    {
        List<String> fileList = new ArrayList<>();
        Session session = null;
        try {
            session = getSession(sshUserDTO);
            fileList = listFile(session, path);
        }
        catch (Exception e) {
            log.error("{}:listFile: err {}", sshUserDTO.getSshHost(), e.getMessage());
        }
        finally {
            if (ObjectUtil.isNotEmpty(session)) {
                session.disconnect();
            }
        }
        createDirAndPermit(sshUserDTO, path);
        return fileList;
    }

    /**
     * Create directory and authorize openlookeng
     *
     * @param sshUserDTO
     * @param path
     */
    public static void createDirAndPermit(SshUserDTO sshUserDTO, String path)
    {
        Session session = null;
        try {
            session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
            Sftp sftp = JschUtil.createSftp(session);
            try {
                Boolean ifExist = sftp.exist(path);
                if (!ifExist) {
                    sftp.mkDirs(path);
                }
            }
            catch (Exception e) {
                sftp.mkDirs(path);
            }
            sftp.close();
            session.disconnect();
            String cmd = "chown -R openlookeng:openlookeng " + path;
            execCmd(sshUserDTO, cmd);
        }
        catch (Exception e) {
            log.error("{}:listFile: err {}", sshUserDTO.getSshHost(), e.getMessage());
        }
        finally {
            if (ObjectUtil.isNotEmpty(session)) {
                session.disconnect();
            }
        }
    }

    public static SshUserDTO getOpenLookengUser(Ecs ecs)
    {
        SshUserDTO sshUserDTO = new SshUserDTO();
        sshUserDTO.setSshHost(ecs.getIp());
        sshUserDTO.setSshPort(ecs.getPort());
        sshUserDTO.setSshUser("openlookeng");
        sshUserDTO.setSshPass("openlookeng");
        return sshUserDTO;
    }
    public static List<String> listFile(Session session, String path)
    {
        Sftp sftp = JschUtil.createSftp(session);
        try {
            Boolean ifExist = sftp.exist(path);
            if (!ifExist) {
                sftp.mkDirs(path);
            }
        }
        catch (Exception e) {
            sftp.mkDirs(path);
        }
        List<String> fileList = sftp.lsFiles(path);
        sftp.close();
        return fileList;
    }

    /**
     * upload file to linux
     *
     * @return
     */
    public static boolean uploadFileToLinux(SshUserDTO sshUserDTO, File file, String path)
    {
        Session session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
        Sftp sftp = JschUtil.createSftp(session);
        try {
            sftp.mkDirs(path);
            boolean res = sftp.upload(path, file);
            return res;
        }
        catch (Exception e) {
            log.error("{}:uploadFileToLinux err:{},file:{}", sshUserDTO.getSshHost(), e.getMessage(), file.getName());
            session.disconnect();
            sftp.close();
            throw new BizException(e.getMessage());
        }
        finally {
            log.error("{}:uploadFileToLinux  finally :{}", sshUserDTO.getSshHost(), file.getName());
            session.disconnect();
            sftp.close();
        }
    }

    public static boolean uploadFileToLinux(Session session, File file, String path)
    {
        Sftp sftp = JschUtil.createSftp(session);
        sftp.mkDirs(path);
        boolean res = sftp.upload(path, file);
        sftp.close();
        return res;
    }

    /**
     * download sftp file
     *
     * @param
     * @param
     * @param path    file path
     * @param
     * @param downUrl Download to local path
     * @throws Exception
     */
    public static void downSftpFile(Session session, String path, String downUrl) throws Exception
    {
        Sftp sftp = JschUtil.createSftp(session);
        try {
            sftp.get(path, downUrl);
            sftp.close();
        }
        catch (JschRuntimeException e) {
            new BizException("No such file");
        }
    }

    /**
     * download sftp file
     *
     * @param
     * @param
     * @param
     * @param
     * @param downUrl Download to local path
     * @throws Exception
     */
    public static void downSftpFile(SshUserDTO sshUserDTO, String src, String downUrl) throws Exception
    {
        try {
            Session session = LinuxShellUtils.getSession(sshUserDTO);
            Sftp sftp = JschUtil.createSftp(session);
            sftp.get(src, downUrl);
            sftp.close();
        }
        catch (JschRuntimeException e) {
            e.printStackTrace();
            throw new BizException("文件下载失败");
        }
    }

    public static String getDeployPath(OpenlookengCluster openlookengCluster)
    {
        String installPath = openlookengCluster.getInstallationPath();
        String version = openlookengCluster.getVersion();
        if (StringUtils.isNotBlank(installPath)) {
            return installPath + "hetu-server-" + version + "/";
        }
        return deployPath + "hetu-server-" + version + "/";
    }

    /**
     * decompress
     *
     * @param path           Unpacked location
     * @param unpackFilePath position before decompression
     * @param
     */
    public static void unpackFile(Session session, String path, String unpackFilePath)
    {
        JschUtil.createSftp(session).mkDirs(path);
        String cmd = "tar -zxvf  " + unpackFilePath + " --strip-components 1 -C " + path;
        JschUtil.exec(session, cmd, Charset.defaultCharset());
        session.disconnect();
    }

    /**
     * decompress
     *
     * @param path
     * @param file
     * @param sshUserDTO
     */
    public static void unpackFile(SshUserDTO sshUserDTO, String path, SysFile file, String unpackFilePath)
    {
        Long startTime = System.currentTimeMillis();
        Session session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
        JschUtil.createSftp(session).mkDirs(path);
        String cmd = ShellConstants.tarCommand + file.getUnpackFilePath(unpackFilePath) + ShellConstants.tarParam + path;
        log.info("{}:perform decompression,file:{}>>>>>>>>>>>>>>>>>>>>>>>,{}", sshUserDTO.getSshHost(), file.getFileName(), cmd);
        String res = JschUtil.exec(session, cmd, Charset.defaultCharset());
        Long endTime = System.currentTimeMillis();
        log.info("{}:Decompression complete,file:{},res:{}>>>>>>>>>>>>>>>>>>>>>>>time consuming:{}", sshUserDTO.getSshHost(), file.getFileName(), res, (endTime - startTime));
        session.disconnect();
    }

    public static void ping(String ip, Integer port)
    {
        Socket client = null;
        try {
            client = new Socket();
            client.connect(new InetSocketAddress(ip, port), 100);
            client.close();
        }
        catch (Exception e) {
            throw new BizException("主机连接失败!");
        }
    }

    public static void delConfigFile(SshUserDTO sshUserDTO, String deployPath)
    {
        String etcPath = deployPath + "etc/";
        String cmd = "rm -rf " + etcPath;
        Session session = JschUtil.getSession(sshUserDTO.getSshHost(), sshUserDTO.getSshPort(), sshUserDTO.getSshUser(), sshUserDTO.getSshPass());
        JschUtil.exec(session, cmd, Charset.defaultCharset());
        session.disconnect();
    }
}
